<template>
  <div class="root" data-flex="">
    <div class="left">
      <div class="title">
        <span>
          <el-checkbox
            :indeterminate="isIndeterminate1"
            :value="unSelectAllCk"
            label="隐藏字段"
            @change="unSelectAll"
          ></el-checkbox>
        </span>
      </div>
      <draggable
        class="list-group"
        :list="unSelected"
        group="listgroup"
        :sort="false"
      >
        <el-checkbox
          class="list-group-item"
          v-for="item in unSelected"
          :key="item.key"
          :label="item.label"
          v-model="item.checked"
        >
        </el-checkbox>
      </draggable>
    </div>
    <div class="m-6" data-flex="dir:top cross:center main:center">
      <span class="m-b-4"
        ><el-button
          size="mini"
          icon="el-icon-arrow-left"
          @click="toLeft"
        ></el-button
      ></span>
      <span class="m-t-4"
        ><el-button
          size="mini"
          icon="el-icon-arrow-right"
          @click="toRigth"
        ></el-button
      ></span>
    </div>
    <div class="right">
      <div class="title">
        <span>
          <el-checkbox
            :indeterminate="isIndeterminate2"
            :value="selectAllCk"
            label="显示字段"
            @change="selectAll"
          ></el-checkbox>
        </span>
      </div>
      <draggable
        class="list-group"
        :list="selected"
        group="listgroup"
        @change="onChange"
      >
        <el-checkbox
          class="list-group-item"
          v-for="item in selected"
          :key="item.key"
          :label="item.label"
          v-model="item.checked"
        >
        </el-checkbox>
      </draggable>
    </div>
  </div>
</template>
<script>
import draggable from 'vuedraggable'
export default {
  name: 'OsDraggable',
  components: { draggable },
  props: {
    value: Array,
    data: Array
  },
  data () {
    return {
      selected: [],
      unSelected: [],
      SelectAllCk: false
    }
  },
  /**
   * 1.支持缓存，只有依赖数据发生改变，才会重新进行计算
   *  2. 不支持异步，当computed内有异步操作时无效，无法监听数据的变化
   *  3.computed 属性值会默认走缓存，计算属性是基于它们的响应式依赖进行缓存的，也就是基于data中声明过或者父组件传递的props中的数据通过计算得到的值
   *  4. 如果一个属性是由其他属性计算而来的，这个属性依赖其他属性，是一个多对一或者一对一，一般用computed
   *  5.如果computed属性属性值是函数，那么默认会走get方法；函数的返回值就是属性的属性值；在computed中的，属性都有一个get和一个set方法，当数据变化时，调用set方法。
   */
  computed: {
    // 设置 indeterminate 状态，只负责样式控制. filter() 方法创建一个新的数组，新数组中的元素是通过检查指定数组中符合条件的所有元素。
    isIndeterminate1 () {
      const num = this.unSelected.filter(item => item.checked).length
      return num > 0 && num < this.unSelected.length
    },
    // every() 方法用于检测数组所有元素是否都符合指定条件
    unSelectAllCk () {
      return this.unSelected.length && this.unSelected.every(item => item.checked)
    },
    isIndeterminate2 () {
      const num = this.selected.filter(item => item.checked).length
      return num > 0 && num < this.selected.length
    },
    selectAllCk () {
      return this.selected.length && this.selected.every(item => item.checked)
    }
  },
  /**
   * 1.不支持缓存，数据变，直接会触发相应的操作；
   *  2.watch支持异步；
   *  3.监听的函数接收两个参数，第一个参数是最新的值；第二个参数是输入之前的值；
   *  4. 当一个属性发生变化时，需要执行对应的操作；一对多；
   *  5. 监听数据必须是data中声明过或者父组件传递过来的props中的数据，当数据变化时，触发其他操作，函数有两个参数，
   *      immediate：组件加载立即触发回调函数执行，
   *      deep: 深度监听，为了发现对象内部值的变化，复杂类型的数据时使用，例如数组中的对象内容的改变，注意监听数组的变动不需要这么做。注意：deep无法监听到数组的变动和对象的新增，参考vue数组变异,只有以响应式的方式触发才会被监听到。
   */
  watch: {
    data: {
      handler () { // handler函数通常情况下只有在监听的属性发生改变时才会触发
        this.init()
      },
      immediate: true // immediate属性为true,在组件创建后，或者说watch被声明和绑定的时立刻执行一次handler函数
    },
    value () {
      this.init()
    }
  },
  methods: {
    init () {
      const selected = []
      const unSelected = []
      this.data.forEach(element => {
        // 对象中的扩展运算符(...)用于取出参数对象中的所有可遍历属性，拷贝到当前对象之中
        const clone = { ...element }
        clone.checked = false
        if (this.value.includes(clone.key)) {
          selected.push(clone)
        } else {
          unSelected.push(clone)
        }
      })
      // 排序
      selected.sort((a, b) => {
        const index1 = this.value.indexOf(a.key)
        const index2 = this.value.indexOf(b.key)
        return index1 - index2
      })
      this.selected = selected
      this.unSelected = unSelected
    },
    unSelectAll (selected) {
      this.unSelected = this.unSelected.map(item => ({ ...item, checked: selected }))
    },
    selectAll (selected) {
      this.selected = this.selected.map(item => ({ ...item, checked: selected }))
    },
    // filter 依据条件过滤，返回新数组，map 返回新数组，与原映射，为原始数组元素调用函数处理后的值
    toLeft () {
      const checked = this.selected.filter(item => item.checked).map(item => ({ key: item.key, label: item.label }))
      this.selected = this.selected.filter(item => !item.checked) // 去掉选中的
      this.unSelected.push(...checked) // 右边加上左边选中的
      this.onChange()
    },
    toRigth () {
      const checked = this.unSelected.filter(item => item.checked).map(item => ({ key: item.key, label: item.label }))
      this.unSelected = this.unSelected.filter(item => !item.checked)
      this.selected.push(...checked)
      this.onChange()
    },
    onChange (e) {
      this.$emit('input', this.selected.map(item => item.key))
      if (e?.moved) {
        this.$emit('sort-change')
      }
    }
  }
}
</script>
<!--
Sass (Syntactically Awesome StyleSheets) 一款css预处理语言,不使用花括号和分号,增加了变量 (variables)、嵌套 (nested rules)、混合 (mixins)、继承(extend)、导入 (inline imports) 等高级功能。
SCSS  SCSS (Sassy CSS)，一款css预处理语言， Sass引入新的语法，其语法完全兼容 CSS3，并且继承了 Sass 的强大功能，需要使用分号和花括号
-->
<style lang="scss" scoped>
.root {
  .left,
  .right {
    border: 1px solid #ebeef5;
    border-radius: 4px;
    overflow: hidden;
    background: #fff;
    display: inline-block;
    vertical-align: middle;
    width: 200px;
    max-height: 100%;
    box-sizing: border-box;

    .title {
      height: 40px;
      line-height: 40px;
      background: #f5f7fa;
      margin: 0;
      padding-left: 15px;
      border-bottom: 1px solid #ebeef5;
      box-sizing: border-box;
      color: #000;
    }

    .list-group {
      margin: 0;
      list-style: none;
      height: 246px;
      overflow: auto;
      box-sizing: border-box;
      padding: 10px 0;

      .list-group-item {
        font-size: 14px;
        cursor: move;
        padding: 2px 16px;
        width: 100%;
        box-sizing: border-box;
        margin-right: 0;

        &:hover {
          background-color: rgb(253, 246, 236);
        }

        &.sortable-chosen {
          background-color: rgb(253, 246, 236);
        }
      }
    }
  }
}
</style>
